/*
 * ModelCC, distributed under ModelCC Shared Software License, www.modelcc.org
 */

package org.modelcc.lexer;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.modelcc.language.LanguageSpecification;
import org.modelcc.language.factory.LanguageSpecificationFactory;
import org.modelcc.language.lexis.LexicalSpecification;
import org.modelcc.language.lexis.TokenOption;
import org.modelcc.language.lexis.TokenSpecification;
import org.modelcc.language.metamodel.SimpleLanguageElement;
import org.modelcc.language.metamodel.CompositeLanguageElement;
import org.modelcc.language.metamodel.LanguageModel;
import org.modelcc.language.metamodel.LanguageElement;
import org.modelcc.lexer.recognizer.PatternRecognizer;

/**
 * ModelCC Parser Generator: Generic interface for lexers.
 * 
 * @author Luis Quesada (lquesada@modelcc.org) & Fernando Berzal (fberzal@modelcc.org)
 */
public abstract class LexerFactory implements Serializable 
{
	/**
	 * Creates a lexer
	 * @param lexis Lexical specification
	 * @return Lexer
	 * @throws LexerException
	 */
    public abstract Lexer createLexer (LexicalSpecification lexis) throws LexerException; 

    /**
     * Creates a lexer
     * @param m the model
     * @return the created lexer
     * @throws LexerException  
     */
    public final Lexer createLexer (LanguageModel m) throws LexerException 
    {
        return createLexer(m, new HashSet<PatternRecognizer>());
    }    

    /**
     * Creates a lexer
     * @param m the model
     * @param skip the skip model.
     * @return the created lexer
     * @throws LexerException  
     */
    public final Lexer createLexer (LanguageModel m, LanguageModel skip) throws LexerException 
	{
        Set<PatternRecognizer> ignore = new HashSet<PatternRecognizer>();
        
        if (skip != null)
            fillIgnore(ignore,skip,skip.getStart());
        
        return createLexer(m,ignore);
    }
    
    /**
     * Fills the ignore set
     * @param ignore the ignore set
     * @param skip the skip model
     * @param el the recursive model element
     */
    private static void fillIgnore (Set<PatternRecognizer> ignore, LanguageModel skip, LanguageElement el) throws LexerException 
    {
        if (el.getClass().equals(CompositeLanguageElement.class)) {
            throw new LexerException("The skip model cannot contain composite elements such as "+el.getElementClass().getCanonicalName());
        } else if (el.getClass().equals(SimpleLanguageElement.class)) {
            ignore.add(((SimpleLanguageElement)el).getPatternRecognizer());
        } else {
            for (LanguageElement element: skip.getSubelements().get(el))
            	fillIgnore(ignore,skip,element);
        }
    }
    
 	
    /**
     * Creates a lexer
     * @param m the model
     * @param ignore the ignore set.
     * @return the created lamb lexer
     * @throws LexerException  
     */
    public final Lexer createLexer (LanguageModel m, Set<PatternRecognizer> ignore) throws LexerException 
    {
        try {
            LanguageSpecificationFactory lsf = new LanguageSpecificationFactory();
            LanguageSpecification language = lsf.create(m);
            LexicalSpecification lexis = language.getLexicalSpecification();
            
            if (ignore != null)
                for (Iterator<PatternRecognizer> ite = ignore.iterator();ite.hasNext();)
                    lexis.addTokenSpecification(new TokenSpecification(null,ite.next(),TokenOption.IGNORE));

            return createLexer(lexis);        
        } catch (Exception e) {
            throw new LexerException("Unable to create lexer", e);
        }
    }
    
    
    
    /**
     * Current lexer factory
     */
    private static LexerFactory factory = new org.modelcc.lexer.lamb.LambLexerFactory(); 
                                       // new org.modelcc.lexer.lamb.LambLexerFactory();
                                       // new org.modelcc.lexer.flex.FlexLexerFactory();

	
	public static LexerFactory getLexerFactory ()
	{
		return LexerFactory.factory;
	}

	public static void setLexerFactory (LexerFactory factory)
	{
		LexerFactory.factory = factory;
	}
    
    /**
     * Creates a lexer (convenience method)
     * @param m the model
     * @return the created lexer
     * @throws LexerException  
     */
    public static Lexer create(LanguageModel m) throws LexerException 
    {    	
        return factory.createLexer(m);
    }
    
    /**
     * Creates a lexer (convenience method)
     * @param m the model
     * @param skip the skip model.
     * @return the created lexer
     * @throws LexerException  
     */
    public static Lexer create (LanguageModel m, LanguageModel skip) throws LexerException 
    {
        return factory.createLexer(m,skip);
    }
      
    /**
     * Creates a lexer (convenience method)
     * @param m the model
     * @param ignore the ignore set.
     * @return the created lexer
     * @throws LexerException  
     */
    public static Lexer create (LanguageModel m,Set<PatternRecognizer> ignore) throws LexerException 
    {
        return factory.createLexer(m,ignore);
    }

    /**
     * Creates a lexer (convenience method)
     * @param lexis the lexical specification
     * @return the created lexer
     * @throws LexerException  
     */
    public static Lexer create (LexicalSpecification lexis) throws LexerException 
    {
        return factory.createLexer(lexis);
    }
    
}
